package com.company.project.exception;

import com.company.project.core.Result;
import com.company.project.core.ResultCode;
import com.company.project.core.ServiceException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashSet;

/**
 * 全局异常捕获接口
 */
@RestControllerAdvice
public class ExceptionHandlerController {

    private final Logger logger = LoggerFactory.getLogger(ExceptionHandlerController.class);

    @Resource
    private HttpServletRequest request;

    @ExceptionHandler(ServiceException.class)
    public Result<Object> handleServiceException(ServiceException e) {
        logger.info(e.getMessage());
        return new Result<>().setCode(ResultCode.FAIL.code()).setMessage(e.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public Result<Object> handleNoHandlerFoundException(NoHandlerFoundException e) {
        return new Result<>().setCode(ResultCode.NOT_FOUND.code()).setMessage("接口不存在!").setMessage("接口 [" + request.getRequestURI() + "] 不存在");
    }

    @ExceptionHandler(ServletException.class)
    public Result<Object> handleServletException(ServletException e) {
        return new Result<>().setCode(ResultCode.FAIL.code()).setMessage(e.getMessage());
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Object> handleConstraintViolationException(ConstraintViolationException e) {
        HashSet<String> error = new HashSet<>();
        for (ConstraintViolation<?> i : e.getConstraintViolations()) {
            error.add(i.getMessage());
        }
        return new Result<>().setCode(ResultCode.FAIL.code()).setMessage("参数错误: " + StringUtils.join(error, " ; "));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        HashSet<String> errorList = new HashSet<>();
        for (ObjectError error : bindingResult.getAllErrors()) {
            if (error instanceof FieldError) {
                FieldError fieldError = (FieldError) error;
                errorList.add(fieldError.getField() + "参数:" + fieldError.getDefaultMessage());
            } else {
                errorList.add(error.getDefaultMessage());
            }
        }
        return new Result<>().setCode(ResultCode.FAIL.code()).setMessage("参数错误: " + StringUtils.join(errorList, " ; "));
    }

    /**
     * 捕获所有异常
     */
    @ExceptionHandler(Exception.class)
    public Result<Object> all(HandlerMethod handler, Exception e) {
        Result<Object> result = new Result<>();
        result.setCode(ResultCode.INTERNAL_SERVER_ERROR.code()).setMessage("服务器内部错误");
        String message;
        if (handler != null) {
            message = String.format("接口 [%s] 出现异常，方法：%s.%s，异常摘要：%s",
                    request.getRequestURI(),
                    handler.getBean().getClass().getName(),
                    handler.getMethod().getName(),
                    e.getMessage());
        } else {
            message = e.getMessage();
        }
        logger.error(message, e);
        return result;
    }
}
